﻿using ExcelUnity.Core;
using ExcelUnity.Importer.Importers;
using ExcelUnity.Importer.Results;
using ExcelUnity.Importer.Wrappers.SheetWraperBuilders;
using NPOI.SS.UserModel;
using System;
using System.Data;
using System.IO;
using System.Linq;

namespace ExcelUnity.Importer
{
    public class DefaultExcelImporter : IExcelImporter
    {
        public ImportResult Import(string filePath, ImportBook importBook, Stream? outPutErrorStream = null)
        {
            using var fileStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read);
            var fileType = CommonHelper.GetFileType(filePath);
            return Import(fileStream, fileType, importBook, outPutErrorStream);
        }

        public ImportResult Import(Stream fileStream, FileType fileType, ImportBook importBook, Stream? outPutErrorStream = null)
        {
            foreach (var item in importBook.Sheets)
            {
                if (item.UserParamValidate().IsNotNullOrWhiteSpace()) throw new ArgumentException(item.UserParamValidate());
            }

            return fileType == FileType.Csv ? ImportCsv(fileStream, importBook, outPutErrorStream)
                                            : ImportExcel(fileStream, fileType, importBook, outPutErrorStream);
        }

        public ImportResult ImportCsv(string filePath, ImportBook importBook, Stream? outPutErrorStream = null)
        {
            using var fileStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read);

            return ImportCsv(fileStream, importBook, outPutErrorStream);
        }

        public ImportResult ImportCsv(Stream fileStream, ImportBook importBook, Stream? outPutErrorStream)
        {
            var csvSheet = CsvHelper.CreateCsvSheet(fileStream);
            var sheetWrapper = CsvSheetWrapperBuilder.BuildSheetWrapperByCsv(importBook.Sheets.First(), csvSheet);

            //回写错误到excel
            if (outPutErrorStream != null)
            {
                if (!sheetWrapper.IsValidated && sheetWrapper.SheetError.IsNullOrWhiteSpace())
                {
                    var errorStyleGenerator = new CsvErrorStyleGenerator(csvSheet);
                    errorStyleGenerator.WriteSheetError(sheetWrapper);
                    csvSheet.Write(outPutErrorStream);
                }
            }
            return new ImportResult(sheetWrapper.ToCsvResultSheet());
        }

        private static ImportResult ImportExcel(Stream fileStream, FileType fileType, ImportBook importBook, Stream? outPutErrorStream)
        {
            IWorkbook? workBook = null;
            try
            {
                workBook = WorkbookHelper.CreateWorkbook(fileType, fileStream);

                var sheetWrappers = ExcelSheetWrapperBuilder.BuildSheetWrappersByExcel(importBook, workBook);

                //回写错误到excel
                if (outPutErrorStream != null)
                {
                    var dataErrorSheets = sheetWrappers.Where(x => !x.IsValidated && x.SheetError.IsNullOrWhiteSpace()).ToList();
                    if (dataErrorSheets.Any())
                    {
                        var errorStyleGenerator = new ExcelErrorStyleGenerator(workBook, importBook.ErrorStyle ?? new ErrorStyle());
                        dataErrorSheets.ForEach(x => errorStyleGenerator.WriteSheetError(workBook.GetSheetAt(x.SheetIndex), x));
                        workBook.Write(outPutErrorStream);
                    }
                }

                var resultSheets = sheetWrappers.Select(x => x.ToResultSheet())
                                                .Union(importBook.Sheets
                                                        .Where(i => !sheetWrappers.Any(x => i.IsMatchSheet(x.SheetIndex, x.SheetName)))
                                                        .Select(x => new ResultSheet(x.SheetIndex, x.SheetName, "未找到对应的Sheet")))
                                                .ToList();
                return new ImportResult(resultSheets);
            }
            catch (Exception ex)
            {
                return new ImportResult(ex, ex.Message);
            }
            finally
            {
                workBook?.Close();
            }

        }
    }
}
